/**
 * @Author: Jiang Zhongxun
 * @Date: 2024-10-06 14:18:54
 * @FilePath: /practiceProject/src/daemon/daemon.c
 * @Description: 守护进程，让服务端保持后台运行，并且能够主动拉起服务端重启。
 * @Copyright (c) 2024 by 蒋仲勋, All Rights Reserved.
 */
#include "../../include/server/daemon.h"
#include "../../include/utils/utils.h"

extern sqlite3 *db; // 操作数据库的句柄
pid_t pid;
// 守护进程标志，控制退出 // 标识是否终止
int is_shutdown = 0;

/**
 * @description: 处理 SIGTERM 信号，用于优雅退出守护进程
 * @param {int} signum 信号
 * @return {*} 无返回值
 */
void signal_handler(int signum)
{
    if (signum == SIGTERM)
    {
        logToLogInfoTable(db, SYSTEMLOG, "收到信号,准备退出守护进程...");
        is_shutdown = 1; // 标志守护进程即将终止
        kill(pid, SIGTERM);
    }
}

/**
 * @description: 创建守护进程
 * @param {*} 无参数
 * @return {*} 无返回值
 */
void create_daemon()
{
    pid_t pid;
    // 1. 创建子进程并退出父进程
    pid = fork();
    if (pid < 0)
    {
        exit(EXIT_FAILURE);
    }
    else if (pid > 0)
    {
        // 父进程退出
        exit(EXIT_SUCCESS);
    }
    // 子进程在执行
    // 2. 创建新会话，脱离控制终端
    if (setsid() < 0)
    {
        exit(EXIT_FAILURE);
    }
    // 3. 忽略SIGHUP信号，处理SIGTERM信号
    signal(SIGHUP, SIG_IGN);
    signal(SIGTERM, signal_handler);
    // 4. 再次 fork，保证守护进程不能重新获取控制终端
    pid = fork();
    if (pid < 0)
    {
        exit(EXIT_FAILURE);
    }
    else if (pid > 0)
    {
        // 父进程退出
        exit(EXIT_SUCCESS);
    }
    // 5. 改变工作目录到根目录，防止占用文件系统
    if (chdir("/") < 0)
    {
        exit(EXIT_FAILURE);
    }
    // 4、重设文件权限掩码
    umask(0); // 开放所有权限
    // 6. 关闭不需要的文件描述符
    // close(STDIN_FILENO);
    // close(STDOUT_FILENO);
    // close(STDERR_FILENO);
    // int fdtablesize = getdtablesize();
    // int fd;
    // for (fd = 0; fd < fdtablesize; fd++)
    // {
    //     close(fd);
    // }

    // openlog("this is our daemonize process: ", LOG_PID, LOG_DAEMON); // /var/log/syslog 日志
    // // 重定向输出到日志文件
    // int log_fd = open("/tmp/daemon.log", O_RDWR | O_CREAT | O_APPEND, 0644);
    // if (log_fd < 0)
    // {
    //     exit(EXIT_FAILURE);
    // }
    // dup2(log_fd, STDOUT_FILENO); // 重定向 stdout
    // dup2(log_fd, STDERR_FILENO); // 重定向 stderr
    // dup2(log_fd, STDIN_FILENO);  // 重定向 stdin
    // close(log_fd);               // 关闭多余文件描述符
    // printf("守护进程创建成功! PID = %d\n", getpid());
}

int main(int argc, char const *argv[])
{
    // 初始化数据库与数据库表
    db = databaseInit();
    int status;
    // 创建守护进程
    create_daemon();
    // 守护进程主循环
    while (1)
    // while (!is_shutdown)
    {
        pid = fork(); // fork 一个子进程去运行 server 程序
        if (pid == 0)
        {
            // syslog(LOG_INFO, "子进程fork成功"); // /var/log/syslog 日志
            // syslog(LOG_INFO, "启动服务端进程"); // /var/log/syslog 日志
            logToLogInfoTable(db, SYSTEMLOG, "子进程fork成功...");
            logToLogInfoTable(db, SYSTEMLOG, "启动服务端进程...");
            // 子进程，执行 server 程序
            char *path = "/home/linux/Desktop/24072/Socket/practiceProject/bin/server/server";
            char *argv[] = {"my_tcp_server", NULL};
            errno = 0;
            execve(path, argv, NULL);
            char buf[64];
            sprintf(buf, "errno: %d", errno);
            // syslog(LOG_ERR, "%s", buf);            // /var/log/syslog 日志
            // syslog(LOG_ERR, "服务端进程启动失败"); // /var/log/syslog 日志
            logToLogInfoTable(db, SYSTEMLOG, buf);
            logToLogInfoTable(db, SYSTEMLOG, "服务端进程启动失败...");
            // 如果 execve 失败，输出错误并退出子进程
            perror("execve failed");
            exit(EXIT_FAILURE);
        }
        else if (pid > 0)
        {
            // 父进程，等待子进程退出
            // syslog(LOG_INFO, "守护进程正在监听服务端进程..."); // /var/log/syslog 日志
            logToLogInfoTable(db, SYSTEMLOG, "守护进程正在监听服务端进程...");
            // waitpid(-1, NULL, 0);
            waitpid(pid, &status, 0);
            if (is_shutdown)
            {
                // syslog(LOG_NOTICE, "子进程已被回收, 即将关闭syslog连接, 守护进程退出"); // /var/log/syslog 日志
                // closelog();
                logToLogInfoTable(db, SYSTEMLOG, "子进程已被回收,守护进程退出...");
                exit(EXIT_SUCCESS);
            }
            // 如果服务进程正常退出或崩溃，等待 3 秒再重新启动
            // syslog(LOG_ERR, "服务端进程终止, 3s后重启..."); // /var/log/syslog 日志
            logToLogInfoTable(db, SYSTEMLOG, "服务端进程终止, 3s后重启...");
            sleep(3);
        }
        else
        {
            // fork 失败，打印错误信息并退出守护进程
            // syslog(LOG_ERR, "子进程fork失败"); // /var/log/syslog 日志
            logToLogInfoTable(db, SYSTEMLOG, "子进程fork失败...");
            perror("fork failed");
            exit(EXIT_FAILURE);
        }
    }
    return EXIT_SUCCESS;
}
